/*
 * Performance.cpp
 *
 *  Created on: 2013-4-23
 *      Author: fasiondog
 */

#include "Performance.h"

namespace hku {

Performance::Performance()
: m_keys({"帐户初始金额",
          "累计投入本金",
          "累计投入资产",
          "累计借入现金",
          "累计借入资产",
          "累计红利",
          "现金余额",
          "未平仓头寸净值",
          "当前总资产",
          "已平仓交易总成本",
          "已平仓净利润总额",
          "单笔交易最大占用现金比例%",
          "交易平均占用现金比例%",
          "未平仓帐户收益率%",
          "已平仓帐户收益率%",
          "帐户年复合收益率%",
          "帐户平均年收益率%",
          "赢利交易赢利总额",
          "亏损交易亏损总额",
          "已平仓交易总数",
          "赢利交易数",
          "亏损交易数",
          "赢利交易比例%",
          "赢利期望值",
          "赢利交易平均赢利",
          "亏损交易平均亏损",
          "平均赢利/平均亏损比例",
          "净赢利/亏损比例",
          "最大单笔赢利",
          "最大单笔盈利百分比%",
          "最大单笔亏损",
          "最大单笔亏损百分比%",
          "赢利交易平均持仓时间",
          "赢利交易最大持仓时间",
          "亏损交易平均持仓时间",
          "亏损交易最大持仓时间",
          "空仓总时间",
          "空仓时间/总时间%",
          "平均空仓时间",
          "最长空仓时间",
          "最大连续赢利笔数",
          "最大连续亏损笔数",
          "最大连续赢利金额",
          "最大连续亏损金额",
          "R乘数期望值",
          "交易机会频率/年",
          "年度期望R乘数",
          "赢利交易平均R乘数",
          "亏损交易平均R乘数",
          "最大单笔赢利R乘数",
          "最大单笔亏损R乘数",
          "最大连续赢利R乘数",
          "最大连续亏损R乘数"}) {
    for (const auto& key : m_keys) {
        m_result[key] = 0.0;
    }
}

Performance::~Performance() {}

bool Performance::exist(const string& key) {
    bool ret = false;
    for (const auto& item : m_keys) {
        if (item == key) {
            ret = true;
            break;
        }
    }
    return ret;
}

Performance& Performance::operator=(const Performance& other) {
    HKU_IF_RETURN(this == &other, *this);
    m_result = other.m_result;
    m_keys = other.m_keys;
    return *this;
}

Performance& Performance::operator=(Performance&& other) {
    HKU_IF_RETURN(this == &other, *this);
    m_result = std::move(other.m_result);
    m_keys = std::move(other.m_keys);
    return *this;
}

void Performance::reset() {
    map_type::iterator iter = m_result.begin();
    for (; iter != m_result.end(); ++iter) {
        if (!std::isnan(iter->second)) {
            iter->second = 0.0;
        }
    }
}

double Performance::get(const string& name) const {
    auto iter = m_result.find(name);
    if (iter != m_result.end()) {
        return iter->second;
    }
    HKU_WARN("Performance - key({}) not exist!", name);
    return Null<double>();
}

PriceList Performance::values() const {
    PriceList result(m_result.size());
    size_t i = 0;
    for (const auto& key : m_keys) {
        result[i++] = m_result.at(key);
    }
    return result;
}

void Performance::addKey(const string& key) {
    m_keys.push_back(key);
    m_result[key] = 0.0;
}

void Performance::setValue(const string& key, double value) {
    m_result[key] = value;
}

string Performance::report() {
    std::stringstream buf;

    buf << std::fixed;
    buf.precision(2);

    buf.setf(std::ios_base::fixed);
    buf.precision(2);
    for (const auto& key : m_keys) {
        buf << key << ": " << m_result.at(key) << std::endl;
    }

    buf.unsetf(std::ostream::floatfield);
    (void)buf.precision();
    return buf.str();
}

void Performance::statistics(const TradeManagerPtr& tm, const Datetime& datetime) {
    // 清除上次统计结果
    reset();

    HKU_INFO_IF_RETURN(!tm, void(), "TradeManagerPtr is Null!");
    HKU_ERROR_IF_RETURN(datetime.isNull(), void(), "Invalid input datetime");
    HKU_ERROR_IF_RETURN(datetime < tm->lastDatetime(), void(),
                        "datetime must >= tm->lastDatetime !");

    int precision = tm->precision();
    m_result["帐户初始金额"] = tm->initCash();
    FundsRecord funds = tm->getFunds(datetime, KQuery::DAY);
    m_result["现金余额"] = funds.cash;
    m_result["累计投入本金"] = funds.base_cash;
    m_result["累计投入资产"] = funds.base_asset;
    m_result["累计借入现金"] = funds.borrow_cash;
    m_result["累计借入资产"] = funds.borrow_asset;
    m_result["未平仓头寸净值"] = funds.market_value;
    m_result["当前总资产"] =
      funds.cash + funds.market_value - funds.borrow_cash - funds.borrow_asset;
    price_t total_money = funds.base_cash + funds.base_asset;

    const TradeRecordList& trade_list = tm->getTradeList();
    TradeRecordList::const_iterator trade_iter = trade_list.begin();
    for (; trade_iter != trade_list.end(); ++trade_iter) {
        if (trade_iter->business == BUSINESS_BONUS) {
            m_result["累计红利"] += trade_iter->realPrice;
        }
    }

    struct CalData {
        CalData()
        : total_duration(0),
          continues(0),
          max_continues(0),
          continues_money(0.0),
          max_continues_money(0.0),
          total_r(0.0),
          max_continues_r(0.0) {}

        int total_duration;           // 总持仓时间
        int continues;                // 当前连续持仓时间
        int max_continues;            // 最大连续持仓数
        price_t continues_money;      // 当前连续持仓利润或损失
        price_t max_continues_money;  // 最大连续持仓利润或损失
        price_t total_r;              // 累计r乘数
        price_t max_continues_r;      // 最大连续盈利或损失的r乘数和
    };

    CalData earn, loss;

    bool pre_earn = true;
    const PositionRecordList& his_position = tm->getHistoryPositionList();
    price_t total_r = 0.0;
    m_result["已平仓交易总数"] = (double)his_position.size();
    PositionRecordList::const_iterator his_iter = his_position.begin();
    for (; his_iter != his_position.end(); ++his_iter) {
        const PositionRecord& pos = *his_iter;
        m_result["已平仓交易总成本"] += pos.totalCost;

        price_t profit = roundEx(pos.sellMoney - pos.totalCost - pos.buyMoney, precision);
        m_result["已平仓净利润总额"] = roundEx(m_result["已平仓净利润总额"] + profit, precision);

        price_t profit_percent = profit / (pos.buyMoney + pos.totalCost) * 100.;

        price_t r = roundEx(profit / pos.totalRisk, precision);
        total_r += r;

        if (profit > 0.0) {
            m_result["赢利交易数"]++;
            m_result["赢利交易赢利总额"] =
              roundEx(profit + m_result["赢利交易赢利总额"], precision);
            if (profit > m_result["最大单笔赢利"]) {
                m_result["最大单笔赢利"] = profit;
            }

            if (profit_percent > m_result["最大单笔盈利百分比%"]) {
                m_result["最大单笔盈利百分比%"] = profit_percent;
            }

            int duration = (pos.cleanDatetime.date() - pos.takeDatetime.date()).days();
            earn.total_duration += duration;
            if (duration > m_result["赢利交易最大持仓时间"]) {
                m_result["赢利交易最大持仓时间"] = duration;
            }

            earn.total_r += r;
            if (r > m_result["最大单笔赢利R乘数"]) {
                m_result["最大单笔赢利R乘数"] = r;
            }

            // 上一笔交易是盈利交易
            if (pre_earn) {
                earn.continues++;
                earn.continues_money = roundEx(profit + earn.continues_money, precision);
                if (earn.continues >= earn.max_continues) {
                    earn.max_continues = earn.continues;
                    if (earn.continues_money > earn.max_continues_money) {
                        earn.max_continues_money = earn.continues_money;
                        earn.max_continues_r += r;
                    }
                }
            } else {
                earn.continues = 1;
                earn.continues_money = profit;
                earn.max_continues = 1;
                if (profit > earn.max_continues_money) {
                    earn.max_continues_money = profit;
                    earn.max_continues_r = r;
                }
            }

            pre_earn = true;

        } else {
            // 没赚钱的，记为亏损交易
            m_result["亏损交易数"]++;
            m_result["亏损交易亏损总额"] =
              roundEx(profit + m_result["亏损交易亏损总额"], precision);
            if (profit < m_result["最大单笔亏损"]) {
                m_result["最大单笔亏损"] = profit;
            }

            if (profit_percent < m_result["最大单笔亏损百分比%"]) {
                m_result["最大单笔亏损百分比%"] = profit_percent;
            }

            int duration = (pos.cleanDatetime.date() - pos.takeDatetime.date()).days();
            loss.total_duration += duration;
            if (duration > m_result["亏损交易最大持仓时间"]) {
                m_result["亏损交易最大持仓时间"] = duration;
            }

            loss.total_r += r;
            if (r < m_result["最大单笔亏损R乘数"]) {
                m_result["最大单笔亏损R乘数"] = r;
            }

            // 上一次是亏损交易
            if (!pre_earn) {
                loss.continues++;
                loss.continues_money = roundEx(profit + loss.continues_money, precision);
                if (loss.continues >= loss.max_continues) {
                    loss.max_continues = loss.continues;
                    if (loss.continues_money < loss.max_continues_money) {
                        loss.max_continues_money = loss.continues_money;
                        loss.max_continues_r += r;
                    }
                }

            } else {
                loss.continues = 1;
                loss.continues_money = profit;
                loss.max_continues = 1;
                if (profit < loss.max_continues_money) {
                    loss.max_continues_money = profit;
                    loss.max_continues_r = r;
                }
            }

            pre_earn = false;
        }
    }

    m_result["最大连续赢利笔数"] = earn.max_continues;
    m_result["最大连续赢利金额"] = earn.max_continues_money;
    m_result["最大连续亏损笔数"] = loss.max_continues;
    m_result["最大连续亏损金额"] = loss.max_continues_money;

    if (m_result["最大连续赢利笔数"] != 0.0) {
        m_result["最大连续赢利R乘数"] =
          roundEx(earn.max_continues_r / m_result["最大连续赢利笔数"], precision);
    }

    if (m_result["最大连续亏损笔数"] != 0.0) {
        m_result["最大连续亏损R乘数"] =
          roundEx(loss.max_continues_r / m_result["最大连续亏损笔数"], precision);
    }

    if (m_result["累计投入本金"] != 0.0) {
        m_result["未平仓帐户收益率%"] =
          100. * (m_result["当前总资产"] / m_result["累计投入本金"] - 1.);
        m_result["已平仓帐户收益率%"] =
          100. * m_result["已平仓净利润总额"] / m_result["累计投入本金"];
    }

    if (m_result["赢利交易数"] != 0.0) {
        m_result["赢利交易平均赢利"] =
          roundEx(m_result["赢利交易赢利总额"] / m_result["赢利交易数"], precision);
        m_result["赢利交易平均持仓时间"] = earn.total_duration / m_result["赢利交易数"];
        m_result["赢利交易平均R乘数"] = roundEx(earn.total_r / m_result["赢利交易数"], precision);
    }

    if (m_result["亏损交易数"] != 0.0) {
        m_result["亏损交易平均亏损"] =
          roundEx(m_result["亏损交易亏损总额"] / m_result["亏损交易数"], precision);
        m_result["亏损交易平均持仓时间"] = loss.total_duration / m_result["亏损交易数"];
        m_result["亏损交易平均R乘数"] = roundEx(loss.total_r / m_result["亏损交易数"], precision);
    }

    if (m_result["亏损交易平均亏损"] != 0.0) {
        m_result["平均赢利/平均亏损比例"] = roundEx(
          m_result["赢利交易平均赢利"] / std::fabs(m_result["亏损交易平均亏损"]), precision);
    }

    if (m_result["已平仓交易总数"] != 0.0) {
        m_result["赢利交易比例%"] = 100 * m_result["赢利交易数"] / m_result["已平仓交易总数"];
        m_result["R乘数期望值"] = roundEx(total_r / m_result["已平仓交易总数"], precision);
    }

    if (m_result["亏损交易亏损总额"] != 0.0) {
        m_result["净赢利/亏损比例"] =
          m_result["赢利交易赢利总额"] / std::fabs(m_result["亏损交易亏损总额"]);
    }

    m_result["赢利期望值"] = 0.01 * m_result["赢利交易比例%"] * m_result["赢利交易平均赢利"] +
                             (1 - 0.01 * m_result["赢利交易比例%"]) * m_result["亏损交易平均亏损"];

    int64_t duration = 0;
    if (tm->firstDatetime() != Null<Datetime>()) {
        if (datetime == Null<Datetime>()) {
            duration = (Datetime::now() - tm->firstDatetime()).days() + 1;
        } else {
            duration = (datetime - tm->firstDatetime()).days() + 1;
        }
    }

    double years = duration / 365.0;

    if (duration > 1) {
        m_result["交易机会频率/年"] = m_result["已平仓交易总数"] / years;
        m_result["年度期望R乘数"] =
          roundEx(m_result["R乘数期望值"] * m_result["交易机会频率/年"], precision);
    }

    if (total_money != 0.0 && years != 0.0) {
        m_result["帐户平均年收益率%"] =
          100 * (((m_result["当前总资产"] / total_money) - 1) / years);
        m_result["帐户年复合收益率%"] =
          100 * ((std::pow(10, (std::log10(m_result["当前总资产"] / total_money) / years)) - 1));
    }

    double max_percent = 0.0, sum_percent = 0.0;
    int trade_number = 0;
    trade_iter = trade_list.begin();
    for (; trade_iter != trade_list.end(); ++trade_iter) {
        if (trade_iter->business == BUSINESS_BUY) {
            trade_number++;
            const TradeRecord& record = *trade_iter;
            price_t hold_cash =
              roundEx(record.realPrice * record.number + record.cost.total, precision);
            price_t total_cash = roundEx(hold_cash + record.cash, precision);
            double percent = (total_cash != 0.0) ? hold_cash / total_cash : 0.0;
            sum_percent += percent;
            if (percent > max_percent) {
                max_percent = percent;
            }
        }
    }

    m_result["单笔交易最大占用现金比例%"] = 100 * max_percent;
    if (trade_number != 0) {
        m_result["交易平均占用现金比例%"] = 100 * sum_percent / trade_number;
    }

    PositionRecordList cur_position = tm->getPositionList();
    int total_short_days = 0;

    if (tm->firstDatetime() != Null<Datetime>()) {
        int short_number = 0;
        int short_days = 0;
        int max_short_days = 0;
        bool pre_short = false;
        Datetime end_day;
        if (datetime == Null<Datetime>()) {
            end_day = Datetime(tm->lastDatetime().date() + bd::days(1));
        } else {
            end_day = Datetime(datetime.date() + bd::days(1));
        }

        DatetimeList day_range = getDateRange(tm->firstDatetime(), end_day);
        DatetimeList::const_iterator day_iter = day_range.begin();
        for (; day_iter != day_range.end(); ++day_iter) {
            bool hold = false;
            his_iter = his_position.begin();
            for (; his_iter != his_position.end(); ++his_iter) {
                if (his_iter->takeDatetime <= *day_iter && *day_iter < his_iter->cleanDatetime) {
                    hold = true;
                    break;
                }
            }

            if (hold) {
                if (pre_short) {
                    short_days = 0;
                    pre_short = false;
                }
                continue;
            }

            // 当前是空仓
            total_short_days++;
            if (pre_short) {
                short_days++;
                if (short_days > max_short_days) {
                    max_short_days = short_days;
                }
            } else {
                short_number++;
                pre_short = true;
            }
        }

        m_result["空仓总时间"] = total_short_days;
        m_result["最长空仓时间"] = max_short_days;
        if (day_range.size() != 0) {
            m_result["空仓时间/总时间%"] = 100 * total_short_days / day_range.size();
        }
        if (short_number != 0) {
            m_result["平均空仓时间"] = total_short_days / short_number;
        }
    }
}

} /* namespace hku */
